package org.zw.android.framework.http;

import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.ParseException;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.HTTP;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;
import org.zw.android.framework.http.ResponseBean.Status;
import org.zw.android.framework.util.StringUtils;

public class HttpExecutorApache implements HttpExecutor {
	
	private enum ResponseType {
		TEXT,STREAM
	}
	
	public HttpExecutorApache(){
		
	}
	
	@Override
	public ResponseBean downloadFile(HttpRequest request) throws Exception {
		return doGet(request, ResponseType.STREAM);
	}
	
	public ResponseBean doGet(HttpRequest request) throws IOException, ClientProtocolException , ParseException{
		return doGet(request,ResponseType.TEXT) ;
	} 
	
	private final ResponseBean doGet(HttpRequest request,ResponseType type) throws IOException, ClientProtocolException , ParseException{
		
		if(request == null){
			ResponseBean bean = new ResponseBean() ;
			bean.setStatus(Status.FAILED);
			bean.setResponse("zhouwei >> http request is null");
			return null ;
		}
		
		// parameter
		Map<String, String> paramters = request.getParameter();
		StringBuilder str = new StringBuilder();
		Set<String> keys = paramters.keySet();
		int index = 0;
		for (String key : keys) {

			if (index > 0) {
				str.append("&");
			}

			str.append(key);
			str.append("=");
			str.append(paramters.get(key));

			index++;
		}
		
		String temp = request.getUrl() ;
		HttpGet get = null ;
		
		// has parameter
		if(!StringUtils.isEmpty(str.toString())){
			String url = temp.indexOf("?") > 0 ? (temp + "&" + str.toString()) : (temp + "?" + str.toString()) ;
			get = new HttpGet(url);
		} else {
			get = new HttpGet(temp);
		}
		
		// header parameter
		Map<String, String> headers = request.getHeaderParameter() ;
		keys 			= headers.keySet() ;
		for(String key : keys){
			get.setHeader(key, headers.get(key));
		}
		
		return doExecute(get,request.getConnectTimeout(),request.getReadTimeout(),type) ;
	} 
	
	@Override
	public ResponseBean doPost(HttpRequest request) throws Exception {
		
		if(request == null){
			ResponseBean bean = new ResponseBean() ;
			bean.setStatus(Status.FAILED);
			bean.setResponse("zhouwei >> http post request is null");
			return null ;
		}
		
		// http post
		HttpPost post = new HttpPost(request.getUrl());

		// header parameter
		Map<String, String> headers = request.getHeaderParameter();
		Set<String> keys = headers.keySet();
		for (String key : keys) {
			post.setHeader(key, headers.get(key));
		}
		
		Map<String, String> paramter 	= request.getParameter() ;
		Map<String, File> files 		= request.getFileParameter() ;
		
		// for file form
		if(files != null && !files.isEmpty()){
			
			MultipartEntity multipartEntity = new MultipartEntity();
			
			// request parameter
			if (paramter != null) {
				for (String name : paramter.keySet()) {
					multipartEntity.addPart(name, new StringBody(String.valueOf(paramter.get(name)), Charset.forName(HTTP.UTF_8)));
				}
			}
			
			// file parameter
			if (files != null) {
				for (String name : files.keySet()) {
					multipartEntity.addPart(name, new FileBody(files.get(name)));
				}
			}
			
			post.setEntity(multipartEntity);
		} 
		// for normal parameter
		else {
			
			if(paramter != null){
				
				List<BasicNameValuePair> params = new LinkedList<BasicNameValuePair>();  
				
				for (String name : paramter.keySet()) {
					params.add(new BasicNameValuePair(name,String.valueOf(paramter.get(name))));
				}
				
				post.setEntity(new UrlEncodedFormEntity(params, "UTF-8"));
			}
		}
		
		// do execute
		return doExecute(post,request.getConnectTimeout(),request.getReadTimeout(),ResponseType.TEXT) ;
	}
	
	@Override
	public ResponseBean doPut(HttpRequest request) throws Exception {
		
		if(request == null){
			ResponseBean bean = new ResponseBean() ;
			bean.setStatus(Status.FAILED);
			bean.setResponse("zhouwei >> http put request is null");
			return null ;
		}
		
		Map<String, String> paramter 	= request.getParameter() ;
		Map<String, File> files 		= request.getFileParameter() ;
		
		// http post
		HttpPut put = new HttpPut(request.getUrl());

		// header parameter
		Map<String, String> headers = request.getHeaderParameter();
		Set<String> keys = headers.keySet();
		for (String key : keys) {
			put.setHeader(key, headers.get(key));
		}
		
		// for file form
		if (files != null && !files.isEmpty()) {

			MultipartEntity multipartEntity = new MultipartEntity();

			// request parameter
			if (paramter != null) {
				for (String name : paramter.keySet()) {
					multipartEntity.addPart(name,
							new StringBody(String.valueOf(paramter.get(name)),Charset.forName(HTTP.UTF_8)));
				}
			}

			// file parameter
			if (files != null) {
				for (String name : files.keySet()) {
					multipartEntity.addPart(name, new FileBody(files.get(name)));
				}
			}

			put.setEntity(multipartEntity);
		}
		// for normal parameter
		else {

			if (paramter != null) {

				List<BasicNameValuePair> params = new LinkedList<BasicNameValuePair>();

				for (String name : paramter.keySet()) {
					params.add(new BasicNameValuePair(name, String.valueOf(paramter.get(name))));
				}
				
				put.setEntity(new UrlEncodedFormEntity(params, "UTF-8"));
			}
		}		
		
		// do execute
		return doExecute(put,request.getConnectTimeout(),request.getReadTimeout(),ResponseType.TEXT) ;
	}

	@Override
	public ResponseBean doDelete(HttpRequest request) throws Exception {
		
		if(request == null){
			ResponseBean bean = new ResponseBean() ;
			bean.setStatus(Status.FAILED);
			bean.setResponse("zhouwei >> http put request is null");
			return null ;
		}
		
		// http post
		HttpDelete delete = new HttpDelete(request.getUrl());

		// header parameter
		Map<String, String> headers = request.getHeaderParameter();
		Set<String> keys = headers.keySet();
		for (String key : keys) {
			delete.setHeader(key, headers.get(key));
		}
		
		Map<String, String> paramter 	= request.getParameter() ;
		HttpParams hp 					= delete.getParams() ;
		keys 							= paramter.keySet() ;
		for(String key : keys){
			hp.setParameter(key, paramter.get(key));
		}
		
		// do execute
		return doExecute(delete,request.getConnectTimeout(),request.getReadTimeout(),ResponseType.TEXT) ;
	}
	
	/** execute http request*/
	private final ResponseBean doExecute(HttpUriRequest httpMethod,
			int connectTimeout,
			int readTimeout,
			ResponseType type) throws IOException,
											ClientProtocolException ,ParseException{
		
		ResponseBean bean = new ResponseBean() ;
		
		// init httpParams
		HttpParams httpParams = new BasicHttpParams();
		if (connectTimeout > 0) {
			HttpConnectionParams.setConnectionTimeout(httpParams,connectTimeout);
		}
		if (readTimeout > 0) {
			HttpConnectionParams.setSoTimeout(httpParams, readTimeout);
		}

		HttpClient httpClient 	= new DefaultHttpClient(httpParams);
		
		// execute request
		HttpContext context 	= new BasicHttpContext();
		HttpResponse response 	= httpClient.execute(httpMethod, context);
		
		int stausCode = response.getStatusLine().getStatusCode();

		if (stausCode != HttpStatus.SC_OK) {

			bean.setStatus(Status.FAILED);
			bean.setHeader(response.getEntity().getContentType());
			bean.setResponse(EntityUtils.toString(response.getEntity()));
			return bean;
		}
		
		HttpEntity entity = response.getEntity() ;
		
		// get response
		bean.setStatus(Status.SUCCESS);
		bean.setHeader(entity.getContentType());
		
		if(type == ResponseType.TEXT){
			bean.setResponse(EntityUtils.toString(entity));
		} else {
			bean.setLength(entity.getContentLength());
			bean.setInputStream(entity.getContent());
		}
		
		return bean;
	}
}
